package com.sxt.text2voice;
import com.baidu.aip.speech.AipSpeech;
import com.baidu.aip.speech.TtsResponse;
import com.baidu.aip.util.Util;

import javazoom.jl.player.Player;

import org.json.JSONObject;
 
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
 
/**
 * 将文本上传至百度服务器，返回语音文件，如output.mp3，并播放MP3文件
 * SDK下载地址：https://ai.baidu.com/file/EDF42F3DB3B4489FA1A88FD1E2BC5D74
 */
public class VoiceCompose {
    // 设置APPID/AK/SK
    // 百度AI开发平台的控制台中创建一个语音应用即可获得
    private static final String APP_ID = "11404467";
    private static final String API_KEY = "quY2ax3X7NwqB7WyZoc9xWvR";
    private static final String SECRET_KEY = "TEMatGKh35RPMhjMGj3ptb6PsEWsl6oV";
 
    private static final AipSpeech aipSpeech = getAipSpeech();
 
    private static String filename = "output.mp3";
 
    private static Player player;
 
    public static void main(String[] args) {
        VoiceCompose voiceCompose = new VoiceCompose();
        String txt = "雨下了一天一夜，军营外的死人坑中，伸出了一只手。\r\n" + 
        		"    巡夜士兵吓的丢了灯笼，连滚带爬的逃回军营上报伍长。\r\n" + 
        		"    伍长和士兵一起，提了长刀，挑着灯，出营查看。\r\n" + 
        		"    两人放眼望去，只见瓢泼大雨中，有一道身影静静坐在死尸堆中。\r\n" + 
        		"    伍长打着灯笼遥遥一照，却见是一名士兵，穿着残破的制式皮甲，倚靠在一堆头颅上，背对着灯火。\r\n" + 
        		"    伍长握紧长刀，沉声问：“前面是何人？”\r\n" + 
        		"    隔着雨幕，只听一道模糊的声音传来：“在下前锋营士兵，受了重伤，身子动不了。”\r\n" + 
        		"    动不了？\r\n" + 
        		"    伍长神色一缓，拎刀上前道：“原来是前锋营的兄弟，来，我扶你起来。”\r\n" + 
        		"    “多谢。”\r\n" + 
        		"    “不客气，去死。”\r\n" + 
        		"    长刀破开雨幕，化作一道冷芒，直往那人脖颈砍去。\r\n" + 
        		"    伍长这一刀凶狠无比，又算准了时机，那人的头颅直接被砍掉，滚落到尸坑深处。\r\n" + 
        		"    一刀了账！\r\n" + 
        		"    伍长狞笑着收回长刀，忽然脸色一变：“什——”\r\n" + 
        		"    一道身影从那人身下的尸堆冲天而起，手中的冷厉寒光抹过伍长的手臂。\r\n" + 
        		"    手臂连同长刀被斩飞出去，一蓬血雾从断臂处爆开，在疾风夜雨中散落不见。\r\n" + 
        		"    剧痛传来，伍长脸上露出难以置信之色，瞳孔中倒映出一道高高跃起的身影。\r\n" + 
        		"    一把短剑在伍长眼前放大。\r\n" + 
        		"    扑的一声，短剑刺入眼眶。\r\n" + 
        		"    剧痛只持续了短暂的一瞬间，然后是一片漆黑。\r\n" + 
        		"    数息之后。\r\n" + 
        		"    少年将短剑从伍长眼眶拔出来。\r\n" + 
        		"    伍长的尸体直挺挺向后倒下去，摔在泥泞中。\r\n" + 
        		"    少年握着短剑，默默站在原地。\r\n" + 
        		"    夜雨噼里啪啦打下来，少年脸上的泥水被冲刷掉，露出一双明亮的眼睛。\r\n" + 
        		"    忽然，少年的眼神变得锐利。\r\n" + 
        		"    在他脚下，伍长的肚子猛地鼓胀起来，肚皮内发出一阵诡异的咕咕声。\r\n" + 
        		"    少年深吸了口气，双手握剑，照着伍长的肚子全力刺进去。\r\n" + 
        		"    一声非人的惨叫传来，伍长肚子上喷出一层黑雾，肚皮下起伏不定，似有什么东西在拼命挣扎。\r\n" + 
        		"    哗啦一声，伍长的肚皮破开，一只漆黑枯瘦的爪子伸了出来。\r\n" + 
        		"    还不等这恐怖的鬼爪有何动作，少年双手握着剑柄猛力一绞。\r\n" + 
        		"    “死！”\r\n" + 
        		"    少年吐出一个字。\r\n" + 
        		"    疯狂扭动的尸体突然僵住，那只鬼爪也不甘的缓缓垂落下去，不再做出任何动作。\r\n" + 
        		"    一切归于寂静。\r\n" + 
        		"    腥臭的黑血，从伍长尸体下面缓缓扩散开来。\r\n" + 
        		"    见了这层黑血，少年才微微松口气，用力拔出短剑。\r\n" + 
        		"    他垂头看着地上的诡异尸体，低声喃喃自语。\r\n" + 
        		"    “这么奇怪的情景，不知道任务奖励会是什么？”\r\n" + 
        		"    少年带着三分期待，轻喝道：“系统！”\r\n" + 
        		"    一息，两息，三息。\r\n" + 
        		"    时间静静流逝，但什么也没有发生。\r\n" + 
        		"    黑夜中，只有风吹雨落之声绵绵不绝于耳。\r\n" + 
        		"    少年意外的歪了歪脑袋，来回望了一圈儿——背后是死人坑，脚下是魔头尸体，前面不远处，一名士兵吓的瘫软在地上，连嘴唇都在发抖。\r\n" + 
        		"    “奇怪。”\r\n" + 
        		"    少年困惑道：“任务还没完成？”\r\n" + 
        		"    系统保持着沉默，说明任务并未完成。\r\n" + 
        		"    少年望着那名士兵，忽然觉得有什么事情被自己忽略了。\r\n" + 
        		"    少年想了想，吃力的往前挪动脚步，却差点跌倒在泥地里。\r\n" + 
        		"    刚才集中心神斩杀魔头，一时不觉得，现在缓过劲来，便发觉身上处处都疼，简直疼的要命。\r\n" + 
        		"    两条腿像灌了铅似的，每挪动一步，都需要用尽全力。\r\n" + 
        		"    这很不对劲。\r\n" + 
        		"    末日来临的那一刻，自己用尽全力斩杀了最终魔主，结果却没退出游戏，反倒是带着一身的伤痛，进入到这个莫名其妙的地方。\r\n" + 
        		"    这到底是哪儿？\r\n" + 
        		"    少年皱着眉，蹒跚着来到士兵面前，行了个军礼。\r\n" + 
        		"    “骁骑营，顾青山前来报道。”\r\n" + 
        		"    “你、你、你杀了上官！”士兵结结巴巴道。\r\n" + 
        		"    “他不是人。”顾青山打量着士兵，口中说道。\r\n" + 
        		"    士兵穿着古老的制式皮甲，这种皮甲连灵力驱动装置都没有——就算再怎么不受重视的部队，也不会穿这种古董玩意儿。\r\n" + 
        		"    顾青山看了看自己身上，居然也是一身的老古董，原本的那些装备都已不翼而飞。\r\n" + 
        		"    这就奇怪了。\r\n" + 
        		"    士兵飞快的朝死人坑撇了一眼，伍长那怪异的尸体一动不动的还躺在泥泞中。\r\n" + 
        		"    士兵犹疑道：“可是……可是你杀他的时候，怎么知道他不是人？”\r\n" + 
        		"    顾青山摊手道：“我只是小心了一些，他就先动了手。”\r\n" + 
        		"    顾青山又走回去，将伍长尸体拖到士兵面前，让士兵仔细查看。\r\n" + 
        		"    “你看，这是一头剥皮血魔。”\r\n" + 
        		"    顾青山用短剑剥开伍长的肚皮，里面却是一头通体漆黑，眼生竖瞳，面目狰狞的怪物。\r\n" + 
        		"    亲眼目睹怪物尸体，让士兵更加为之震撼。\r\n" + 
        		"    士兵回想起这几天莫名其妙死掉的同伴们，心中后怕之余，终于对少年有了些感激。\r\n" + 
        		"    士兵稳住心神，问道：“你叫顾青山？”\r\n" + 
        		"    “对。”\r\n" + 
        		"    “骁骑营？”\r\n" + 
        		"    “对。”\r\n" + 
        		"    “腰牌呢？”\r\n" + 
        		"    顾青山就摸出腰牌，自己看了看，才丢给对方查验。\r\n" + 
        		"    这腰牌比他想象的要重。\r\n" + 
        		"    以当今的炼器技术，身份腰牌明明可以炼制的如同一页纸那么轻，怎么自己身上这腰牌，重的跟秤砣似的。\r\n" + 
        		"    少年心中越来越疑惑。\r\n" + 
        		"    士兵接过腰牌细细一看，上面果然是“骁骑营顾青山”六个字，整面腰牌缭绕着丝丝充满生机的灵光。\r\n" + 
        		"    腰牌是真的。\r\n" + 
        		"    士兵长出了一口气，脸上的紧张褪去，显露出疲惫之色：“总算来了个活人，快，外面不能久呆，你快随我进军营。”\r\n" + 
        		"    这就对了，顾青山微微颔首道：“好的。”\r\n" + 
        		"    士兵将腰牌递回来，转身朝营地走去。\r\n" + 
        		"    顾青山接过腰牌，又细细看了一眼。\r\n" + 
        		"    ——这腰牌不仅沉重，整个质地居然是青铜的，表面粗陋的刻着几个字，笨拙又难看，完全是过时的样式。\r\n" + 
        		"    过时的样式……\r\n" + 
        		"    顾青山只觉得心中有一道亮光划过，一股说不出的恐惧袭上心头。\r\n" + 
        		"    他猛的抬起头，目光锁定在前方的士兵身上。\r\n" + 
        		"    古老的制式皮甲。\r\n" + 
        		"    答案是如此匪夷所思，以至于顾青山忍不住大声问道：“兄弟，现在是哪一年？”\r\n" + 
        		"    士兵扭过头，奇怪的望着他：“现在是承平六百八十一年啊。”\r\n" + 
        		"    顾青山怔住。\r\n" + 
        		"    突然，瀑布一般的数据流化作疯狂穿梭的蓝光，如汹涌浪潮一般，从顾青山眼目中闪过。\r\n" + 
        		"    轰——\r\n" + 
        		"    冰冷的机械声音骤然响起。\r\n" + 
        		"    “当前时间确认为承平末年。”\r\n" + 
        		"    “时间流稳定，确认脱离时空乱流。”\r\n" + 
        		"    “判断：成功逃离末日！”\r\n" + 
        		"    “身份重置成功，当前身份为：人族前锋军骁骑营士兵。”\r\n" + 
        		"    系统终于启动，但顾青山心中毫无喜悦之情，只觉得眼前这一幕简直不能相信。\r\n" + 
        		"    怎么会是承平末年，这一年游戏都还未开始！\r\n" + 
        		"    这是只存在于游戏背景和历史之中的过去时光，现实世界的人类都还没有正式进入这个恐怖的游戏异世界。\r\n" + 
        		"    等到玩家进入游戏，已经是一年之后的事情。\r\n" + 
        		"    难道自己回到了游戏开始之前？\r\n" + 
        		"    那么现实世界中呢，难道现实中自己也回到了过去？\r\n" + 
        		"    顾青山一颗心狠狠抽紧，不由得抬头四顾。\r\n" + 
        		"    前方，士兵走的更远了，已经跨过军营大门。\r\n" + 
        		"    营房的门上，隐匿法阵的灵芒偶尔冒出来一缕。\r\n" + 
        		"    越过军营朝远方望去，昏暗荒凉的原野深处，依稀能看到模糊的巨大身影，在暴雨中一闪而没。\r\n" + 
        		"    顾青山慢慢抬起手臂，张开嘴狠狠咬了一口。\r\n" + 
        		"    一排清晰的牙印出现在胳膊上，咬的比较深的地方，渐渐渗出血珠。\r\n" + 
        		"    好疼！\r\n" + 
        		"    这不是做梦！\r\n" + 
        		"    顾青山像个雕塑一般，一动不动的站在滂沱大雨中，任凭冰冷的雨水从天而落，浇透了身体。";
        List<String> strList = getStrList(txt,500);//按500长度分割
        for(String text : strList) {
        	
        	if(!voiceCompose.getMP3ByText(text)){
                System.out.println("转换失败");
            }else{
                voiceCompose.playMP3();
            }
        }
    }
 
    private static AipSpeech getAipSpeech(){
        // 初始化一个AipSpeech
        AipSpeech aipSpeech = new AipSpeech(APP_ID, API_KEY, SECRET_KEY);
 
        // 可选：设置网络连接参数
        aipSpeech.setConnectionTimeoutInMillis(2000);
        aipSpeech.setSocketTimeoutInMillis(60000);
 
        // 可选：设置代理服务器地址, http和socket二选一，或者均不设置
        //aipSpeech.setHttpProxy("proxy_host", proxy_port);  // 设置http代理
        //aipSpeech.setSocketProxy("proxy_host", proxy_port);  // 设置socket代理
 
        // 可选：设置log4j日志输出格式，若不设置，则使用默认配置
        // 也可以直接通过jvm启动参数设置此环境变量
        // System.setProperty("aip.log4j.conf", "path/to/your/log4j.properties");
 
        return aipSpeech;
    }
	 /**
	  * 
	  * @param inputString
	  * @param length
	  * @return
	  */
    public static List<String> getStrList(String inputString, int length) {
        int size = inputString.length() / length;
        if (inputString.length() % length != 0) {
            size += 1;
        }
        return getStrList(inputString, length, size);
    }
    /**
     * 	按指定长度分割字符串
     * @param inputString
     * @param length
     * @param size
     * @return
     */
    public static List<String> getStrList(String inputString, int length,
            int size) {
        List<String> list = new ArrayList<String>();
        for (int index = 0; index < size; index++) {
            String childStr = substring(inputString, index * length,
                    (index + 1) * length);
            list.add(childStr);
        }
        return list;
    }
    /**
     * 
     * @param str
     * @param f
     * @param t
     * @return
     */
    public static String substring(String str, int f, int t) {
        if (f > str.length())
            return null;
        if (t > str.length()) {
            return str.substring(f, str.length());
        } else {
            return str.substring(f, t);
        }
    }
    
    /**
     * 	将文字转为MP3文件，需联网，依靠百度语音合成
     * @param text
     * @return 是否成功
     */
    public boolean getMP3ByText(String text){
        player = null;
        // 设置可选参数
        HashMap<String, Object> options = new HashMap<>();
        // 语速，取值0-9，默认为5中语速
        options.put("spd", "5");
        // 音调，取值0-9，默认为5中语调
        options.put("pit", "5");
        // 音量，取值0-15，默认为5中音量
        options.put("vol", "5");
        // 发音人选择, 0为女声，1为男声，3为情感合成-度逍遥，4为情感合成-度丫丫，默认为普通女
        options.put("per", "4");
 
        // 调用接口
        // text 合成的文本，使用UTF-8编码。小于2048个中文字或者英文数字。（文本在百度服务器内转换为GBK后，长度必须小于4096字节）
        // lang 固定值zh。语言选择,目前只有中英文混合模式，填写固定值zh
        // ctp 客户端类型选择，web端填写固定值1
        TtsResponse res = aipSpeech.synthesis(text, "zh", 1, options);
        // 如果合成成功,下行数据为二进制语音文件，包含在data中。 如果合成出现错误，则会填充返回值到result中。
        // 若请求错误，服务器将返回的JSON文本包含以下参数：
        // error_code：错误码。
        // error_msg：错误描述信息，帮助理解和解决发生的错误。
        byte[] data = res.getData();
        JSONObject res1 = res.getResult();
        if (data != null) {
            try {
                Util.writeBytesToFileSystem(data, filename);
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
 
        if (res1 != null) {
            System.out.println(res1.toString(2));
        }
 
        return true;
    }
	 /**
	  * 播放MP3
	  */
    public void playMP3(){
        try {
            BufferedInputStream buffer = new BufferedInputStream(new FileInputStream(filename));
            // 需导入javazoom.jl.player.Player，下载地址http://www.javazoom.net/javalayer/sources/jlayer1.0.1.zip
            player = new Player(buffer);
            player.play();
        } catch (Exception e) {
            System.out.println(e);
        }
    }
	 /**
	  *    	播放状态
	  * @return
	  */
    public String playerStatus(){
        if(player == null){
            return "null";
        }else if(player.isComplete()){
            return "played";
        }else{
            return "playing";
        }
    }
}